/*
 * This program is free software: you can redistribute it and/or modify it under
 * the terms of the GNU General Public License as published by the Free Software
 * Foundation, either version 3 of the License, or (at your option) any later
 * version.
 *
 * This program is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
 * details.
 *
 * You should have received a copy of the GNU General Public License along with
 * this program. If not, see <http://www.gnu.org/licenses/>.
 */
package ru.papergames.web.blines.battle.network;

import ru.common.network.clientcore.*;
import ru.common.util.ThreadPoolManager;
import ru.common.util.Util;
import ru.papergames.web.blines.battle.network.ClientWebServer.ClientWebServerState;

import ru.papergames.web.blines.battle.network.serverpackets.*;

import java.nio.ByteBuffer;
import java.util.logging.Logger;

/**
 * Stateful Packet Handler<BR>
 * The Stateful approach prevents the server from handling inconsistent packets, examples:<BR>
 * <li>Clients sends a MoveToLocation packet without having a character attached. (Potential errors handling the packet).</li>
 * <li>Clients sends a RequestAuthLogin being already authed. (Potential exploit).</li>
 * <BR><BR>
 * Note: If for a given exception a packet needs to be handled on more then one state, then it should be added to all these states.
 *
 * @author KenM
 */
public final class ClientWebServerGamePacketHandler
        implements
        IPacketHandler<ClientWebServer>,
        IClientFactory<ClientWebServer>,
        IMMOExecutor<ClientWebServer>,
        WebServerServerPacketTypes {
    private static final Logger _log = Logger.getLogger(ClientWebServerGamePacketHandler.class.getName());

    // implementation

    public ReceivablePacket<ClientWebServer> handlePacket(ByteBuffer buf, ClientWebServer client) {
        if (client.isDetached())
            return null;

        int packetId = buf.get() & 0xFF;
        int id3;

        ReceivablePacket<ClientWebServer> msg = null;
        ClientWebServerState state = client.getState();

        switch (state) {
            case CONNECTED:
                switch (packetId) {
                    case WEB_SERVER_AUTH_PASSED_PACKET_ID: // WebServerAuthPassed
                        msg = new WebServerAuthPassed();
                        break;
                    case WEB_SERVER_AUTH_FAILED_PACKET_ID: // WebServerAuthFailed
                        msg = new WebServerAuthFailed();
                        break;
                    case FORCE_RECONNECT_REQUEST_PACKET_ID:
                        msg = new ForceReconnectRequest();
                        break;
                    default:
                        printDebug(packetId, buf, state, client);
                        break;
                }
                break;
            case AUTHED:
                switch (packetId) {
                    case FLASH_USER_CONNECTED_PACKET_ID:
                        msg = new FlashUserConnected();
                        break;
                    case FLASH_USER_DISCONNECTED_PACKET_ID:
                        msg = new FlashUserDisconnected();
                        break;
                    case BATTLE_CREATED_PACKET_ID:
                        msg = new BattleCreated();
                        break;
                    case BATTLE_CREATE_FAILED_PACKET_ID:
                        msg = new BattleCreateFailed();
                        break;
                    case BATTLE_FINISH_NOTIFICATION_PACKET_ID:
                        msg = new BattleFinishNotification();
                        break;
                    case BATTLE_START_NOTIFICATION_PACKET_ID:
                        msg = new BattleStartNotification();
                        break;
//                    case JOIN_EXISTING_BATTLE_RESPONSE_PACKET_ID:
//                        msg = new JoinOsidePcPlayerToExistingBattleResponse();
//                        break;
                    case FORCE_RECONNECT_REQUEST_PACKET_ID:
                        msg = new ForceReconnectRequest();
                        break;
                    case 0x00:
                        //msg = new Logout();
//                        break;
//                    case 0x0c:
//                        msg = new CharacterCreate();
//                        break;
                    default:
                        printDebug(packetId, buf, state, client);
                        break;
                }
                break;
            case IN_GAME:
                switch (packetId) {
                    case 0x01: // AuthorizationPassed
//                        msg = new AuthorizationPassed();
//                        break;
//                    case 0x03: // AuthorizationPassed
//                        msg = new AuthorizationFailed();
//                        break;
//                    case 0xD1:
//                        msg = new TestPlayerMoved();
//                        break;
//                    case 0x04:
//                        msg = new PingResponse();
//                        break;
//                    case BATTLE_MOVE_PACKET_ID:
//                        msg = new BattleMove();
//                        break;
//                    case 0xD2:
//                        msg = new TestMovementUp();
//                        break;
//                    case 0xD3:
//                        msg = new TestMovementDown();
//                        break;
                    default:
                        this.printDebug(packetId, buf, state, client);
                        break;
                }
                break;
        }
        return msg;
    }

    private void printDebug(int opcode, ByteBuffer buf, ClientWebServerState state, ClientWebServer client) {

        int size = buf.remaining();
        System.out.println("[Battles]Unknown Packet: " + Integer.toHexString(opcode) + " on State: " + state.name() + " Client: " + client.toString());
        byte[] array = new byte[size];
        buf.get(array);
        System.out.println(Util.printData(array, size));
    }

    private void printDebugDoubleOpcode(int opcode, int id2, ByteBuffer buf, ClientWebServerState state, ClientWebServer client) {

        int size = buf.remaining();
        System.out.println("[Battles]Unknown Packet: " + Integer.toHexString(opcode) + ":" + Integer.toHexString(id2) + " on State: " + state.name() + " Client: " + client.toString());
        byte[] array = new byte[size];
        buf.get(array);
        System.out.println(Util.printData(array, size));
    }

    // impl

    public ClientWebServer create(MMOConnection<ClientWebServer> con) {
        return new ClientWebServer(con);
    }

    public void execute(ReceivablePacket<ClientWebServer> rp) {
        if (rp.getClient().getState() == ClientWebServerState.AUTHED) {
            ThreadPoolManager.getInstance().executeBattleServersPacket(rp);
        } else {
            rp.run();
        }
    }
}
